<?php
/**
 * 
 * Modules class file
 * 
 * @author		Stijn Van Minnebruggen
 * @version		1.0.0
 * 
 */

class Modules
{
	
	/**
	 * Main variable with all the modules
	 * 
	 * @var		array
	 */
	
	private $_modules;
	
	
	/**
	 * Current module
	 * 
	 * @var		string
	 */
	
	private $_currModule;
	
	
	/**
	 * Holds general module settings
	 * 
	 * @var		array
	 */
	
	private $_cfg;
	
	
	/**
	 * Holds all the messages that should be exposed
	 * 
	 * @var		array
	 */
	
	private $_messages = array();
	
	
	/**
	 * The current view (overview, frm, del)
	 * 
	 * @var		array
	 */
	
	public $_view;
	
	
	/**
	 * Module data from the current module
	 * 
	 * @var		array
	 */
	
	public $_data = array();
	
	
	/**
	 * Constructor function
	 * 
	 * @return	bool
	 */
	
	public function __construct()
	{
		// required
			global $cosa, $m, $a;
		
		// general module settings
			$this->_cfg['modules'] = 'modules';
			$this->_cfg['controller'] = 'module';
			$this->_cfg['view'] = 'view';
			$this->_cfg['lang'] = 'lang';
		
		// set module paths
			$this->_cfg['root_path'] = ROOT_PATH.'/'.$this->_cfg['modules'];
			$this->_cfg['root_url'] = $cosa->system('base-url').'/'.$this->_cfg['modules'];
			$this->_cfg['system_path'] = ROOT_PATH.'/kernel/'.$this->_cfg['modules'];
			$this->_cfg['system_url'] = $cosa->system('base-url').'/kernel/'.$this->_cfg['modules'];
		
		// get installed modules
			$this->_modules = $this->getUserModules();
		
		// get current module
			global $m, $lang;
			$this->_currModule = ($m) ? $m : false;
			if(!$this->_checkUserPermission()) {
				$m = DEFAULT_MODULE;
				$a = 'overview';
				$this->addMessage($lang->get('module-noperm'));
				$this->_currModule = $m;
				$this->_view = $a;
			}
		
		// return
			return true;
		
	}
	
	
	/**
	 * Get current module id
	 * 
	 * @return	int
	 */
	
	public function id()
	{
		return $this->_modules[$this->_currModule]['id'];
	}
	
	
	/**
	 * Print module links
	 * 
	 * @param	bool	$system
	 * @param	bool	$includeUL
	 * @return	void
	 */
	
	public function printNavigation($type=false, $includeUL=true)
	{
		// start list
			global $cosa, $m;
			if($includeUL) echo '<ul>'."\n";
		
		// loop listed modules
			$i = 1;
			$num = $this->numModules($type);
			$sorted = $this->_modules;
			ksort($sorted);
			foreach($sorted as $path => $mod) {
				if(!$type || ($type && $mod['type'] == $type)) {
					$class = ($i == $num) ? ' class="last"' : '';
					$selected = ($m == $path) ? ' class="selected"' : '';
					echo "\t\t\t".'<li'.$class.'><a href="'.$cosa->system('base-url').'/?m='.$path.'"'.$selected.'>'.$mod['name'].'</a></li>'."\n";
					$i++;
				}
			}
		
		// end list
			if($includeUL) echo "\t\t".'</ul>'."\n";
		
	}
	
	
	/**
	 * Get the number of modules
	 * 
	 * @param	bool	$system
	 * @return	int
	 */
	
	public function numModules($type)
	{
		$num=0;
		foreach($this->_modules as $path => $mod) {
			if($mod['type'] == $type) $num++;
		}
		return $num;
	}
	
	
	/**
	 * Get or check the controller file
	 * 
	 * @param	string		$path
	 * @param	bool		$die
	 * @param	string		$fullPath
	 * @return	string
	 */
	
	public function controller($path=false, $die=true, $fullPath=false)
	{
		// required
			global $cosa;
			$p = ($path) ? $path : $this->_currModule;
		
		// check if module is system module (if needed)
			$modules_path = '';
			if(!$fullPath) {
				$isSystemModule = $this->isSystemModule($p);
				$modules_path = ($isSystemModule) ? $this->_cfg['system_path'].'/' : $this->_cfg['root_path'].'/';
			}
		
		// check controller file
			$file = $modules_path.$p.'/'.$this->_cfg['controller'].'.php';
			if(file_exists($file)) return $file;
		
		// return false or die
			if($die) $cosa->kill('Can not init module.');
			else return false;
		
	}
	
	
	/**
	 * Get or check the view file
	 * 
	 * @param	string		$path
	 * @param	bool		$die
	 * @param	string		$fullPath
	 * @param	string		$overrideS
	 * @param	bool		$overrideA
	 * @return	string
	 */
	
	public function view($path=false, $die=true, $fullPath=false, $overrideS=false, $overrideA=false)
	{
		// required
			global $cosa, $s, $a;
			$p = ($path) ? $path : $this->_currModule;
		
		// check if module is system module (if needed)
			$modules_path = '';
			if(!$fullPath) {
				$isSystemModule = $this->isSystemModule($p);
				$modules_path = ($isSystemModule) ? $this->_cfg['system_path'].'/' : $this->_cfg['root_path'].'/';
			}
		
		// check view file
			$useA = (!$overrideA && $a) ? $a : 'overview';
			$useS = ($overrideS) ? $overrideS : $s;
			$file = $modules_path.$p.'/'.$this->_cfg['view'].'/'.$useS.'_'.$useA.'.php';
			if(file_exists($file)) return $file;
		
		// return false or die
			if($die) $cosa->oops('Can not load module or module incomplete!');
			else return false;
		
	}
	
	
	/**
	 * Set the view
	 * 
	 * @param	string		$view
	 * @return	bool
	 */
	
	public function setView($view)
	{
		global $a;
		$va = array('overview', 'frm', 'del');
		if(in_array($view, $va)) {
			$this->_view = $view;
			$a = $view;
			return true;
		} else return false;
	}
	
	
	/**
	 * Get or check the language file
	 * 
	 * @param	string		$path
	 * @param	bool		$die
	 * @param	bool		$fullPath
	 * @return	string
	 */
	
	public function lang($path=false, $die=true, $fullPath=false)
	{
		// required
			global $cosa, $lang;
			$p = ($path) ? $path : $this->_currModule;
		
		// check if module is system module
			$modules_path = '';
			if(!$fullPath) {
				$isSystemModule = $this->isSystemModule($p);
				$modules_path = ($isSystemModule) ? $this->_cfg['system_path'] : $this->_cfg['root_path'];
			}
		
		// check current language
			$file = $modules_path.'/'.$p.'/'.$this->_cfg['lang'].'/'.$cosa->language.'.php';
			if(file_exists($file)) return $file;
		
		// check default language (required)
			$file = $modules_path.'/'.$p.'/'.$this->_cfg['lang'].'/'.DEFAULT_LANG.'.php';
			if(file_exists($file)) return $file;
		
		// return false or die
			if($die) $cosa->kill('Can not load module.');
			else return false;
		
	}
	
	
	/**
	 * Init the current module
	 * 
	 * @return	bool
	 */
	
	public function init()
	{
		global $module;
		if(!class_exists($this->_currModule)) return false;
		$module = new $this->_currModule();
		return true;
	}
	
	
	/**
	 * Get installed system modules
	 * 
	 * @return	array
	 */
	
	public function getInstalledModules()
	{
		global $cosa, $db;
		$mod = array();
		$sql = "SELECT * FROM ".$db->table('modules')." ORDER BY name";
		$res = mysql_query($sql) or $cosa->oops('Error while fetching modules');
		while($row = mysql_fetch_array($res, MYSQL_ASSOC)) $mod[] = $row;
		return $mod;
	}
	
	
	/**
	 * Get all available modules
	 * 
	 * @return	array
	 */
	
	public function getAllModules()
	{
		// required
			global $cosa;
			$all_modules = array();
			$paths = array(	'system'	=> $this->_cfg['system_path']
							,'standard'	=> $this->_cfg['root_path']
							);
		
		// get all modules
			foreach($paths as $type => $path) {
				if($h = opendir($path)) {
					while(false !== ($module = readdir($h))) {
						if(is_dir($path.'/'.$module) && (substr($module, 0, 1) != '.')) {
							$controller = $this->controller($path.'/'.$module, false, true);
							$lang = $this->lang($path.'/'.$module, false, true);
							if($controller && $lang) $all_modules[] = array('path' => $module, 'type' => $type);
						}
					}
					closedir($h);
				}
			}
		
		// return modules
			return $all_modules;
		
	}
	
	
	/**
	 * Get modules by usergroup
	 * 
	 * @param	int		$group_id
	 * @return	array
	 */
	
	public function getUserModules($group_id=false)
	{
		global $user;
		return $user->getModules($group_id);
	}
	
	
	/**
	 * Get module information
	 * 
	 * @param	string	$path
	 * @param	bool	$archived
	 * @return	array
	 */
	
	public function getModuleInformation($path=false, $archived=true)
	{
		if(!$path) $path = $this->_currModule;
		if($archived && !empty($this->_data[$path])) return $this->_data[$path];
		$isSystemModule = $this->isSystemModule($path);
		$data = $this->_readModuleData($path);
		$this->_data[$path] = $data;
		return $data;
	}
	
	
	/**
	 * Read module data
	 * 
	 * @param	string	$path
	 * @return	array
	 */
	
	private function _readModuleData($path)
	{
		// check module file
			$mod_file = $this->controller($path, false);
			if(!$mod_file) return false;
		
		// read file
			$h = fopen($mod_file, "rb");
			$c = stream_get_contents($h);
			preg_match("/@name(.*)/", $c, $name);
			preg_match("/@description(.*)/", $c, $description);
			preg_match("/@version(.*)/", $c, $version);
			preg_match("/@style(.*)/", $c, $style);
			preg_match("/@script(.*)/", $c, $script);
			preg_match("/@author(.*)/", $c, $author);
			preg_match("/@author_url(.*)/", $c, $author_url);
			preg_match("/@module_url(.*)/", $c, $module_url);
		
		// subs
			$i=1; $subs = array();
			while(preg_match("/@sub".$i."(.*)/", $c, $sub)) {
				$subs[] = trim($sub[1]); $i++;
			}
		
		// fix vars
			if(empty($style)) $style = array('', '');
			if(empty($script)) $script = array('', '');
				
		// return data
			fclose($h);
			$module_data = array('name'			=> trim($name[1])
								,'description'	=> trim($description[1])
								,'version'		=> trim($version[1])
								,'style'		=> trim($style[1])
								,'script'		=> trim($script[1])
								,'author'		=> trim($author[1])
								,'author_url'	=> trim($author_url[1])
								,'module_url'	=> trim($module_url[1])
								,'subs'			=> $subs
								,'path'			=> $path
								);
			return $module_data;
		
	}
	
	
	/**
	 * Create module sidebar
	 * 
	 * @return	bool
	 */
	
	public function createSidebar()
	{
		// required
			global $cosa, $m, $s, $lang;
			$mdata = $this->getModuleInformation();
			if(!isset($mdata['subs']) || empty($mdata['subs'])) return false;
		
		// start sidebar
			echo '<div id="column"><ul>'."\n";
		
		// loop subs
			foreach($mdata['subs'] as $k => $sub) {
				echo "\t\t".'<li><a href="'.$cosa->system('base-url').'?m='.$m.'&s='.$sub.'"';
				if($s == $sub) echo ' class="sel"';
				echo '>'.$lang->get($sub).'</a></li>'."\n";
			}
		
		// end sidebar
			echo '</ul></div>'."\n";
			return true;
		
	}
	
	
	/**
	 * Get the default sub module
	 * 
	 * @return 	string
	 */
	
	public function defaultSub()
	{
		$data = $this->getModuleInformation();
		return (isset($data['subs'][0])) ? $data['subs'][0] : $this->_currModule;
	}
	
	
	/**
	 * Print the current breadcrumb
	 * 
	 * @return	void
	 */
	
	public function breadcrumb()
	{
		// REQUIRED
			global $lang, $m, $s, $add, $edit, $del;
			$splitter = ' &rarr; ';
		
		// CHECK FIRST PART
			if($this->isSystemModule()) { $lang->put('modules-system'); } else { $lang->put('modules'); }
		
		// FURTHER
			if($m) echo $splitter.$lang->get($m);
			if($s && $s != $m) echo $splitter.$lang->get($s);
			if($add) echo $splitter.$lang->get('add');
			if($edit) echo $splitter.$lang->get('edit');
			if($del) echo $splitter.$lang->get('del');
		
	}
	
	
	/**
	 * Delete item from module
	 * 
	 * @param	int		$id
	 * @return	bool
	 */
	
	public function del($id)
	{
		global $module, $s;
		$this->setView('overview');
		eval("return (method_exists(\$module, '".$s."_del')) ? \$module->".$s."_del(\$id) : false;");
	}
	
	
	/**
	 * Add item to module
	 * 
	 * @param	array	$postVars
	 * @return	bool
	 */
	
	public function add($postVars)
	{
		global $module, $s;
		$this->setView('overview');
		eval("return (method_exists(\$module, '".$s."_add')) ? \$module->".$s."_add(\$postVars) : false;");
	}
	
	
	/**
	 * Edit and item from module
	 * 
	 * @param	array	$postVars
	 * @param	int		$id
	 * @return	bool
	 */
	
	public function edit($postVars, $id)
	{
		global $module, $s;
		$this->setView('overview');
		eval("return (method_exists(\$module, '".$s."_edit')) ? \$module->".$s."_edit(\$postVars, \$id) : false;");
	}
	
	
	/**
	 * Add a message to the pool
	 * 
	 * @param	string	$msg
	 * @return	bool
	 */
	
	public function addMessage($msg)
	{
		if(in_array($msg, $this->_messages)) return false;
		$this->_messages[] = $msg;
		return true;
	}
	
	
	/**
	 * Show module messages if there are any
	 * 
	 * @return	void
	 */
	
	public function printMessages()
	{
		$display = (count($this->_messages)) ? '' : ' style="display:none;"';
		$this->_showedMessages = array();
		echo '<ul class="messages"'.$display.'>'."\n";
		foreach($this->_messages as $k => $message) {
			echo '<li>'.$message.'</li>'."\n";
			$this->_showedMessages[] = $message;
		}
		echo '</ul>'."\n";
	}
	
	
	/**
	 * Create html header
	 * 
	 * @return	string
	 */
	
	public function htmlHeader()
	{
		// required
			global $cosa;
			$html = '';
		
		// generate html headers
			foreach($this->_modules as $path => $module) {
				
				// check if module is system module
					$isSystemModule = $this->isSystemModule($path);
					$modules_path = ($isSystemModule) ? $this->_cfg['system_path'] : $this->_cfg['root_path'];
					$modules_url = ($isSystemModule) ? $this->_cfg['system_url'] : $this->_cfg['root_url'];
				
				// fetch styles
					$filePath = $modules_path.'/'.$path.'/'.$module['style'];
					$fileUrl = $modules_url.'/'.$path.'/'.$module['style'];
					if($module['style'] && file_exists($filePath)) {
						$html .= '<style type="text/css" media="screen"> @import url('.$fileUrl.'); </style>'."\n\t";
					}
				
				// fetch scripts
					$filePath = $modules_path.'/'.$path.'/'.$module['script'];
					$fileUrl = $modules_url.'/'.$path.'/'.$module['script'];
					if($module['script'] && file_exists($filePath)) {
						$html .= '<script type="text/javascript" src="'.$fileUrl.'"></script>'."\n\t";
					}
				
			}
		
		// return value
			return $html;
		
	}
	
	
	/**
	 * Get current module path
	 * 
	 * @return	string
	 */
	
	public function currModule()
	{
		return $this->_currModule;
	}
	
	
	/**
	 * Check if module is system module
	 * 
	 * @param	string	$path
	 * @return	bool
	 */
	
	public function isSystemModule($path=false)
	{
		global $m;
		if(!$path) $path = $m;
		return is_dir($this->_cfg['system_path'].'/'.$path);
	}
	
	
	/**
	 * Check if user has permission to this module
	 * 
	 * @param	string	$var
	 * @return	bool
	 */
	
	public function _checkUserPermission()
	{
		return key_exists($this->_currModule, $this->_modules);
	}
	
	
	/**
	 * Print module blocks
	 * 
	 * @param	string	$type
	 * @return	void
	 */
	
	public function printBlocks($type=false)
	{
		global $cosa;
		$sorted = $this->_modules;
		ksort($sorted);
		foreach($sorted as $path => $module) {
			if(!$type || ($type && $type == $module['type'])) {
				$modLink = $cosa->system('base-url').'/?m='.$path;
				echo '<div class="module_block" id="mod_'.$path.'">'."\n\t";
				echo '<h2><a href="'.$modLink.'">'.$module['name'].'</a></h2>'."\n\t";
				echo '<p>Module content should be here...</p><p><a href="'.$modLink.'">view module</a> &rarr;</p>'."\n";
				echo '</div>'."\n";
			}
		}
	}
	
	
	/**
	 * Install a module
	 * 
	 * @param	string	$path
	 * @return	bool
	 */
	
	public function install($path)
	{
		// required
			global $cosa, $user, $db, $lang;
		
		// execute module install script
			$controller = $this->controller($path);
			include $controller;
			if(!class_exists($path)) die('class doesnt exists');
			$mod = new $path();
			if(is_callable(array($mod, 'install'))) $inst = $mod->install();
			if($inst !== true) {
				$this->addMessage($inst);
				return false;
			}
		
		// get module information
			$mdata = $this->getModuleInformation($path);
			$type = ($this->isSystemModule($path)) ? 'system' : 'standard';
			if(empty($mdata)) return false;
		
		// add to module database
			$sql = "INSERT INTO ".$db->table('modules')."
					SET	name			= '".addslashes($mdata['name'])."'
						,description	= '".addslashes($mdata['description'])."'
						,version		= '".addslashes($mdata['version'])."'
						,author			= '".addslashes($mdata['author'])."'
						,author_url		= '".addslashes($mdata['author_url'])."'
						,module_url		= '".addslashes($mdata['module_url'])."'
						,path			= '".addslashes($mdata['path'])."'
						,script			= '".addslashes($mdata['script'])."'
						,style			= '".addslashes($mdata['style'])."'
						,installed_at	= NOW()
						,installed_by	= '".$user->id()."'
						,type			= '".addslashes($type)."'
					";
			if(!@mysql_query($sql)) return false;
			$new_id = mysql_insert_id();
		
		// add to admin permissions
			$sql = "INSERT INTO ".$db->table('modules_perm')." SET module_id = '".$new_id."', users_group_id = '1'";
			if(!@mysql_query($sql)) return false;
		
		// return bool
			return true;
		
	}
	
	
	/**
	 * Uninstall a module
	 * 
	 * @param	string	$path
	 * @return	bool
	 */
	
	public function uninstall($path)
	{
		return true;
	}
	
	
	/**
	 * Destruct function, adds messages not yet showed from the pool
	 * 
	 * @return	void
	 */
	
	public function __destruct()
	{
		$addMessage = array();
		foreach($this->_messages as $k => $message) {
			if(!isset($this->_showedMessages)) $this->_showedMessages = array();
			if(!in_array($message, $this->_showedMessages)) {
				$addMessage[] = $message;
			}
		}
		if(count($addMessage)) {
			echo '<script type="text/javascript">'."\n";
			foreach($addMessage as $k => $message) {
				echo 'addMessage("'.$message.'");'."\n";
			}
			echo 'showMessages();'."\n";
			echo '</script>'."\n";
		}
	}
	
	
	/**
	 * Get module settings
	 * 
	 * @return	array
	 */
	
	public function getSettings()
	{
		global $cosa, $db;
		$settings = array();
		$sql = "SELECT * FROM ".$db->table('settings')." WHERE type = 'modules' AND type_id = '".$this->id()."'";
		$res = mysql_query($sql) or $this->kill('Could not fetch module settings.', 'db');
		while($row = mysql_fetch_array($res)) $settings[$row['key']] = stripslashes($row['value']);
		return $settings;
	}
	
	
	/**
	 * Set module settings
	 * 
	 * @param	string $key
	 * @param	multiple $value
	 * @return	bool
	 */
	
	public function setSettings($key, $value)
	{
		// required
			global $cosa, $db;
		
		// check if exists
			$sql = "SELECT id FROM ".$db->table('settings')." WHERE type = 'modules' AND type_id = '".$this->id()."' AND `key` = '".addslashes($key)."' LIMIT 1";
			$res = mysql_query($sql) or $this->kill('Could not set module settings.', 'db');
			$new = !mysql_num_rows($res);
			if(!$new) {
				$row = mysql_fetch_array($res);
				$original_id = $row['id'];
			}
		
		// insert into settings db
			$sql = ($new) ? "INSERT INTO " : "UPDATE ";
			$sql .= $db->table('settings')." SET type = 'modules', type_id = '".$this->id()."', `key` = '".addslashes($key)."', `value` = '".addslashes($value)."' ";
			$sql .= ($new) ? '' : "WHERE id = '".addslashes($original_id)."'";
		
		// query and return result
			mysql_query($sql) or die(mysql_error());
			return true;
		
	}
	
	
}


?>